package com.varian.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import com.varian.mybatis.base.BaseServiceImpl;
import com.varian.security.util.SecurityUtil;
import com.varian.system.constant.SystemConstant;
import com.varian.system.convert.SystemConvert;
import com.varian.system.dto.SysMenuDTO;
import com.varian.system.dto.SysMenuListDTO;
import com.varian.system.dto.SysMenuTreeDTO;
import com.varian.system.dto.SysMenuWeightsDTO;
import com.varian.system.entity.SysMenu;
import com.varian.system.enums.MenuTypeEnum;
import com.varian.system.mapper.RoleMenuMapper;
import com.varian.system.mapper.SysMenuMapper;
import com.varian.system.service.ISysMenuService;
import com.varian.system.vo.SysMenuVO;
import com.varian.tool.Func;
import com.varian.tool.enums.CommonStatusEnum;
import com.varian.tool.exception.BizException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author ben
 * @since 2024/7/2
 */
@Service
@RequiredArgsConstructor
public class SysMenuServiceImpl extends BaseServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    private final SystemConvert baseConvert;
    private final RoleMenuMapper roleMenuMapper;

    @Override
    public boolean add(SysMenuDTO dto) {
        Func.validate(dto, dto.getMenuType().getGroup());
        SysMenu menu = baseConvert.toEntity(dto);
        validateUniqueness(menu);
        return save(menu);
    }

    @Override
    public boolean modify(Long id, SysMenuDTO dto) {
        Func.validate(dto, dto.getMenuType().getGroup());
        validateExistence(id);
        SysMenu menu = baseConvert.toEntity(id, dto);
        validateUniqueness(menu);
        return updateById(menu);
    }

    @Override
    public SysMenuVO detail(Long id) {
        SysMenu menu = validateExistence(id);
        return baseConvert.toVO(menu);
    }

    @Override
    public List<SysMenuVO> tree(SysMenuTreeDTO dto) {
        List<SysMenu> list = list(lambdaQuery().getWrapper()
                .eq(dto.getMenuStatus() != null, SysMenu::getMenuStatus, dto.getMenuStatus())
                .in(Boolean.TRUE.equals(dto.getDirectoryAndMenu()), SysMenu::getMenuType, MenuTypeEnum.DIRECTORY, MenuTypeEnum.MENU)
                .in(!SecurityUtil.isAdmin(), SysMenu::getPermissionCode, SecurityUtil.getPermissions())
                .orderByAsc(SysMenu::getMenuType)
                .orderByAsc(SysMenu::getMenuWeight)
        );
        List<SysMenuVO> result = Func.convert(list, baseConvert::toVO);
        return Func.treeBuild(result);
    }

    @Override
    public boolean delete(Long id) {
        validateExistence(id);
        boolean childrenExisted = baseMapper.exists(lambdaQuery().getWrapper().eq(SysMenu::getParentId, id));
        Assert.isFalse(childrenExisted, () -> BizException.of(SystemConstant.MENU_HAS_CHILDREN));
        if (removeById(id)) {
            roleMenuMapper.deleteByMenuId(id);
        }
        return true;
    }

    @Override
    public List<SysMenuVO> treeByUser() {
        List<SysMenu> list = list(lambdaQuery().getWrapper()
                .eq(SysMenu::getMenuStatus, CommonStatusEnum.ENABLED)
                .in(SysMenu::getMenuType, MenuTypeEnum.DIRECTORY, MenuTypeEnum.MENU, MenuTypeEnum.LINK)
                .in(!SecurityUtil.isAdmin(), SysMenu::getPermissionCode, SecurityUtil.getPermissions())
                .orderByAsc(SysMenu::getMenuWeight)
        );
        List<SysMenuVO> result = Func.convert(list, baseConvert::toVO);
        return Func.treeBuild(result);
    }

    @Override
    public List<SysMenuVO> list(SysMenuListDTO dto) {
        Long parentId = ObjUtil.defaultIfNull(dto.getParentId(), Func.ROOT_ID);
        List<SysMenu> menus = baseMapper.selectList(lambdaQuery().getWrapper().eq(SysMenu::getParentId, parentId));
        return CollUtil.map(menus, baseConvert::toVO, true);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean weights(Long parentId, List<SysMenuWeightsDTO> list) {
        validateExistence(parentId);
        CollUtil.forEach(list, ((dto, index) -> {
            update(lambdaUpdate()
                    .set(SysMenu::getMenuWeight, dto.getMenuWeight())
                    .getWrapper()
                    .eq(SysMenu::getId, dto.getId())
                    .eq(SysMenu::getParentId, parentId)
            );
        }));
        return true;
    }

    @Override
    protected SysMenu validateExistence(Long id) {
        SysMenu menu = baseMapper.selectById(id);
        Assert.notNull(menu, () -> BizException.of(SystemConstant.MENU_NOT_EXIST));
        return menu;
    }

    @Override
    protected void validateUniqueness(SysMenu menu) {
        boolean existed = baseMapper.exists(lambdaQuery().getWrapper()
                .eq(SysMenu::getPermissionCode, menu.getPermissionCode())
                .ne(menu.getId() != null, SysMenu::getId, menu.getId()));
        Assert.isFalse(existed, () -> BizException.of(SystemConstant.MENU_PERMISSION_CODE_EXIST));
    }

}
